package com.michael.ims.gateway.component;

import com.alibaba.fastjson.JSONObject;
import com.michael.common.ex.BusinessException;
import com.michael.io.Error;
import com.michael.io.RespData;
import com.michael.io.RespStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Type;
import java.util.List;

@Slf4j
@Component
public class WebClientTemplate {
    private WebClient webClient = WebClient.builder().build();
    @Value("${com.michael.domain.ims-app}")
    private String imsAppDomain;

    public WebClientTemplate() {

    }

    public <T> Mono<T> handle(String uri, Object request, Class<T> responseType) {
        return webClient.post().uri(imsAppDomain + uri)
                .headers(httpHeaders -> {
                    httpHeaders.add("Authorization", "michael");
                    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                })
                .bodyValue(request)
                .exchange()
                .flatMap(clientResponse -> clientResponse.bodyToMono(new ParameterizedTypeReference<RespData<T>>() {
                    @Override
                    public Type getType() {
                        return ParameterizedTypeImpl.make(RespData.class, new Type[]{responseType}, null);
                    }
                }))
                .map(response -> {
                    if (!StringUtils.equals(response.getStatus(), RespStatus.SUCCESS)) {
                        Error error = response.getError();
                        //todo
                        throw new BusinessException(error.getCode(), error.getMessage());
                    }
                    return response.getBody();
                });
    }

    public <R> Mono<JSONObject> handle(String uri, Publisher<R> publisher, Class<R> elementClass) {
        return webClient.post().uri(imsAppDomain + uri)
                .headers(httpHeaders -> {
                    httpHeaders.add("Authorization", "michael");
                    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                })
                .body(publisher, elementClass)
                .exchange()
                .flatMap(clientResponse -> clientResponse.bodyToMono(new ParameterizedTypeReference<RespData<JSONObject>>() {
                }))
                .map(response -> {
                    if (!StringUtils.equals(response.getStatus(), RespStatus.SUCCESS)) {
                        Error error = response.getError();
                        //todo
                        throw new BusinessException(error.getCode(), error.getMessage());
                    }
                    return response.getBody();
                });
    }

    public <T> Mono<List<T>> handleList(String uri, Object request, Class<T> responseType) {
        return webClient.post().uri(imsAppDomain + uri)
                .headers(httpHeaders -> {
                    httpHeaders.add("Authorization", "michael");
                    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                })
                .bodyValue(request)
                .exchange()
                .flatMap(clientResponse -> clientResponse.bodyToMono(new ParameterizedTypeReference<RespData<List<T>>>() {
                    @Override
                    public Type getType() {
                        ParameterizedTypeImpl data = ParameterizedTypeImpl.make(List.class, new Type[]{responseType}, null);
                        ParameterizedTypeImpl type = ParameterizedTypeImpl.make(RespData.class, new Type[]{data}, null);
                        return type;
                    }
                }))
                .map(response -> {
                    if (!StringUtils.equals(response.getStatus(), RespStatus.SUCCESS)) {
                        Error error = response.getError();
                        //todo
                        throw new BusinessException(error.getCode(), error.getMessage());
                    }
                    return response.getBody();
                });
    }

    public <T> Mono<List<T>> HandleGetList(String uri, Class<T> responseType) {
        return webClient.get().uri(imsAppDomain + uri)
                .headers(httpHeaders -> {
                    httpHeaders.add("Authorization", "michael");
                    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                })
                .exchange()
                .flatMap(clientResponse -> clientResponse.bodyToMono(new ParameterizedTypeReference<RespData<List<T>>>() {
                    @Override
                    public Type getType() {
                        ParameterizedTypeImpl data = ParameterizedTypeImpl.make(List.class, new Type[]{responseType}, null);
                        ParameterizedTypeImpl type = ParameterizedTypeImpl.make(RespData.class, new Type[]{data}, null);
                        return type;
                    }
                }))
                .map(response -> {
                    if (!StringUtils.equals(response.getStatus(), RespStatus.SUCCESS)) {
                        Error error = response.getError();
                        //todo
                        throw new BusinessException(error.getCode(), error.getMessage());
                    }
                    return response.getBody();
                });
    }
}
